package com.clj.fastble.bluetooth;

import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.data.BleMsg;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.exception.OtherException;
import com.clj.fastble.utils.BleLog;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.LinkedList;
import java.util.Queue;

/**
 * SplitWriter
 */
public class SplitWriter {
    private EventRunner mHandlerThread;
    private EventHandler mHandler;

    private BleBluetooth mBleBluetooth;
    private String mUuid_service;
    private String mUuid_write;
    private byte[] mData;
    private int mCount;
    private boolean mSendNextWhenLastSuccess;
    private long mIntervalBetweenTwoPackage;
    private BleWriteCallback mCallback;
    private Queue<byte[]> mDataQueue;
    private int mTotalNum;

    public SplitWriter() {
        mHandlerThread = EventRunner.create("SplitWriter");
        mHandlerThread.run();

        mHandler =
                new EventHandler(EventRunner.getMainEventRunner()) {
                    @Override
                    protected void processEvent(InnerEvent event) {
                        super.processEvent(event);
                        if (event.eventId == BleMsg.MSG_SPLIT_WRITE_NEXT) {
                            write();
                        }
                    }
                };
    }


    /**
     * splitWrite
     * @param bleBluetooth bleBluetooth
     * @param uuid_service uuid_service
     * @param uuid_write uuid_write
     * @param data data
     * @param sendNextWhenLastSuccess sendNextWhenLastSuccess
     * @param intervalBetweenTwoPackage intervalBetweenTwoPackage
     * @param callback callback
     */
    public void splitWrite(
            BleBluetooth bleBluetooth,
            String uuid_service,
            String uuid_write,
            byte[] data,
            boolean sendNextWhenLastSuccess,
            long intervalBetweenTwoPackage,
            BleWriteCallback callback) {
        mBleBluetooth = bleBluetooth;
        mUuid_service = uuid_service;
        mUuid_write = uuid_write;
        mData = data;
        mSendNextWhenLastSuccess = sendNextWhenLastSuccess;
        mIntervalBetweenTwoPackage = intervalBetweenTwoPackage;
        mCount = BleManager.getInstance().getSplitWriteNum();
        mCallback = callback;

        splitWrite();
    }

    private void splitWrite() {
        if (mData == null) {
            throw new IllegalArgumentException("data is Null!");
        }
        if (mCount < 1) {
            throw new IllegalArgumentException("split count should higher than 0!");
        }
        mDataQueue = splitByte(mData, mCount);
        mTotalNum = mDataQueue.size();
        write();
    }

    private void write() {
        if (mDataQueue.peek() == null) {
            release();
            return;
        }

        byte[] data = mDataQueue.poll();
        mBleBluetooth
                .newBleConnector()
                .withUUIDString(mUuid_service, mUuid_write)
                .writeCharacteristic(
                        data,
                        new BleWriteCallback() {
                            @Override
                            public void onWriteSuccess(int current, int total, byte[] justWrite) {
                                int position = mTotalNum - mDataQueue.size();
                                if (mCallback != null) {
                                    mCallback.onWriteSuccess(position, mTotalNum, justWrite);
                                }
                                if (mSendNextWhenLastSuccess) {
                                    InnerEvent event = InnerEvent.get(BleMsg.MSG_SPLIT_WRITE_NEXT);
                                    mHandler.sendEvent(event, mIntervalBetweenTwoPackage);
                                }
                            }

                            @Override
                            public void onWriteFailure(BleException exception) {
                                if (mCallback != null) {
                                    mCallback.onWriteFailure(
                                            new OtherException(
                                                    "exception occur while writing: " + exception.getDescription()));
                                }
                                if (mSendNextWhenLastSuccess) {
                                    InnerEvent event = InnerEvent.get(BleMsg.MSG_SPLIT_WRITE_NEXT);
                                    mHandler.sendEvent(event, mIntervalBetweenTwoPackage);
                                }
                            }
                        },
                        mUuid_write);

        if (!mSendNextWhenLastSuccess) {
            InnerEvent message = InnerEvent.get(BleMsg.MSG_SPLIT_WRITE_NEXT);
            mHandler.sendEvent(message, mIntervalBetweenTwoPackage);
        }
    }

    private void release() {
        mHandlerThread.stop();
        mHandler.removeAllEvent();
    }

    private static Queue<byte[]> splitByte(byte[] data, int count) {
        if (count > 20) {
            BleLog.worn("Be careful: split count beyond 20! Ensure MTU higher than 23!");
        }
        Queue<byte[]> byteQueue = new LinkedList<>();
        int pkgCount;
        if (data.length % count == 0) {
            pkgCount = data.length / count;
        } else {
            pkgCount = Math.round(data.length / count + 1);
        }

        if (pkgCount > 0) {
            for (int i = 0; i < pkgCount; i++) {
                byte[] dataPkg;
                int ji;
                if (pkgCount == 1 || i == pkgCount - 1) {
                    ji = data.length % count == 0 ? count : data.length % count;
                    System.arraycopy(data, i * count, dataPkg = new byte[ji], 0, ji);
                } else {
                    System.arraycopy(data, i * count, dataPkg = new byte[count], 0, count);
                }
                byteQueue.offer(dataPkg);
            }
        }

        return byteQueue;
    }
}
